package com.citywithincity.db.meta.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.citywithincity.db.interfaces.SqlDriver;
import com.citywithincity.db.meta.ColumnStruct;
import com.citywithincity.db.meta.DbStructFactory;
import com.citywithincity.db.meta.TableStruct;
import com.citywithincity.db.meta.TableStructFilter;
import com.citywithincity.utils.StringUtils;

public abstract class AbsDbStructFactory implements DbStructFactory {

	protected static Log logger = LogFactory.getLog(AbsDbStructFactory.class);

	private Map<String, TableStruct> tableStructs;

	private SqlDriver driver;

	private int db;

	public AbsDbStructFactory(SqlDriver driver) {
		this.driver = driver;
	}

	public void setDb(int db) {
		this.db = db;
	}

	public int getDb() {
		return db;
	}

	@Override
	public TableStruct getTableStruct(String tableName) {
		if (tableStructs == null) {
			// 这里需要将所有表结构展示出来
			tableStructs = new TreeMap<String, TableStruct>();
		}
		String key = tableName.toUpperCase();
		TableStruct struct = tableStructs.get(key);
		if (struct == null) {
			List<ColumnStruct> list = getColumns(tableName);
			if (list == null) {
				throw new RuntimeException(tableName + "字段为空或者表" + tableName + "不存在");
			}
			struct = new TableStruct(list, driver.protectTableName(tableName), getComment(tableName));
			tableStructs.put(key, struct);
		}
		return struct;
	}

	private Map<String, String> commentsMap;

	@Override
	public String getComment(String tableName) {
		Map<String, String> result = this.commentsMap;
		if (result == null) {
			result = getTableCommentMap();
			commentsMap = result;
		}
		return result.get(tableName.toUpperCase());
	}

	protected abstract Map<String, String> getTableCommentMap();

	private Map<String, List<ColumnStruct>> columnMap = new HashMap<String, List<ColumnStruct>>();
	
	public List<ColumnStruct> getColumns(String tableName){
		List<ColumnStruct> list = columnMap.get(tableName.toUpperCase());
		if(list==null){
			list = getColumnStructs(tableName);
			columnMap.put(tableName, list);
		}
		return list;
	}


	protected abstract List<ColumnStruct> getColumnStructs(String tableName);

	/*
	protected List<ColumnStruct> getColumnStructs(String tableName){
		Connection connection = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			connection = Db.getDataSourceProvider(getDb()).getConnection();
			ps = connection.prepareStatement("SELECT * FROM "+tableName+" WHERE 1=2");
			rs = ps.executeQuery();
			
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}finally {
			DbUtil.close(rs, ps);
			DbUtil.close(connection);
		}
	}
	
	public List<ColumnStruct> parse(ResultSet rs,SqlDriver driver) throws SQLException, ClassNotFoundException{
		ResultSetMetaData data = rs.getMetaData();
		for(int i=1 ,count = data.getColumnCount(); i <= count; ++i){
			ColumnStruct columnStruct = new ColumnStruct();
			columnStruct.setName(data.getColumnName(i));
			columnStruct.setClazz(Class.forName(data.getColumnClassName(i)));
			columnStruct.setType(data.getColumnType(i));
			
		}
	
	}
	*/
	@Override
	public Collection<TableStruct> getTableStructs(int type) {
		if (type == STRUCT_USING) {
			return tableStructs.values();
		}
		/*
		if (columnMap == null) {
			columnMap = createColumnMap();
		}*/
		/*if (tableStructs == null) {
			tableStructs = new HashMap<String, TableStruct>();
		}
		List<TableStruct> list = new ArrayList<TableStruct>(columnMap.size());
		// 比较有哪些是没有的
		for (Entry<String, List<ColumnStruct>> columnEntry : columnMap.entrySet()) {
			String key = columnEntry.getKey();
			TableStruct tableStruct = tableStructs.get(key);
			if (tableStruct == null) {
				tableStruct = new TableStruct(columnEntry.getValue(), key, getComment(key));
			}
			list.add(tableStruct);
		}
		return list;*/
		return null;
	}

	private static final char BLANK = ' ';

	@Override
	public String createTable(TableStruct struct) {
		StringBuilder sb = new StringBuilder();
		sb.append("CREATE TABLE ").append(struct.getName()).append(" (\n");
		Collection<ColumnStruct> columns = struct.getColumns();
		int index = 0;
		int size = columns.size() - 1;
		for (ColumnStruct column : columns) {
			sb.append(column.getName()).append(BLANK).append(getDbType(column));
			// 默认值
			if (column.getDefaultValue() != null) {
				sb.append(" default ").append(column.getDefaultValue());
			}
			// 是否为空
			if (column.isNullable()) {
				sb.append(" null");
			} else {
				sb.append(" not null");
			}

			if (index == size) {
				sb.append("\n");
			} else {
				sb.append(",\n");
			}
			index++;
		}
		sb.append(");\n");

		/**
		 * 主键
		 */
		if (struct.getPks().length > 0) {
			if (struct.getPks().length == 1 && struct.getPks()[0].isAuto()) {
				ColumnStruct columnStruct = struct.getPks()[0];
				//add auto primary key
			} else {
				sb.append("ALERT TABLE ").append(struct.getName()).append(" ADD PRIMARY KEY (");
				boolean first = true;
				for (ColumnStruct columnStruct : struct.getPks()) {
					if (first) {
						first = false;
					} else {
						sb.append(',');
					}
					sb.append(columnStruct.getName());
				}
				sb.append(");\n");
			}
		}

		/**
		 * 表注释
		 */
		if (!StringUtils.isEmpty(struct.getComment())) {
			sb.append("COMMENT ON TABLE ").append(struct.getName()).append(" IS '").append(struct.getComment())
					.append("';\n");
		}

		/**
		 * 注释
		 */
		for (ColumnStruct columnStruct : struct.getColumns()) {
			if (StringUtils.isEmpty(columnStruct.getComment()))
				continue;
			sb.append("COMMENT ON COLUMN ").append(struct.getName()).append('.').append(columnStruct.getName())
					.append(" IS '").append(columnStruct.getComment()).append("';\n");
		}
		/**
		 * 唯一约束
		 */

		/**
		 * 索引
		 */


		logger.info(sb.toString());
		return sb.toString();
	}

	
	@Override
	public Collection<TableStruct> query(TableStructFilter filter) {
		Collection<TableStruct> structs = tableStructs.values();
		List<TableStruct> list = new ArrayList<TableStruct>();
		for (TableStruct tableStruct : structs) {
			if(filter.accept(tableStruct)){
				list.add(tableStruct);
			}
		}
		return list;
	}

	protected abstract String getDbType(ColumnStruct column);
}
